package services

import (
	"context"
	"encoding/hex"
	"errors"
	"fmt"
	"math/rand"
	"strconv"
	"time"
	"wxapi/common"
	"wxapi/internal/svc"
	"wxapi/models"

	"gitee.com/dromara/carbon/v2"
	"github.com/go-redis/redis/v8"
)

func s2uKey(session string) string {
	return fmt.Sprintf("Session:S2U:%s", session)
}

func u2sKey(userId int64) string {
	return fmt.Sprintf("Session:U2S:%d", userId)
}

func CreateSession(ctx context.Context, svcCtx *svc.ServiceContext, user *models.User) (session string, err error) {
	bytes := common.AesEncryptCBC([]byte(fmt.Sprintf("%d-%s-%s-%d", user.ID, user.Nickname, user.CreatedAt.ToDateTimeString(), rand.Int31())), []byte(svcCtx.Config.EncryptKey))
	session = hex.EncodeToString(bytes)
	fmt.Println(session)

	_, err = svcCtx.Redis.Set(ctx, s2uKey(session), user.ID, carbon.SecondsPerWeek*time.Second).Result()
	if err != nil {
		return "", err
	}
	_, err = svcCtx.Redis.Set(ctx, u2sKey(user.ID), session, carbon.SecondsPerWeek*time.Second).Result()
	if err != nil {
		return "", err
	}

	return session, nil
}

func RemoveSession(ctx context.Context, svcCtx *svc.ServiceContext, user *models.User) (err error) {
	session, err := svcCtx.Redis.Get(ctx, u2sKey(user.ID)).Result()
	if err != nil && !errors.Is(err, redis.Nil) {
		return err
	}

	s2u := s2uKey(session)
	u2s := u2sKey(user.ID)

	_, err = svcCtx.Redis.Del(ctx, s2u).Result()
	if err != nil {
		return err
	}
	_, err = svcCtx.Redis.Del(ctx, u2s).Result()
	if err != nil {
		return err
	}

	return nil
}

func GetUserIdBySession(ctx context.Context, svc *svc.ServiceContext, session string) (userId int64, err error) {
	s2u := s2uKey(session)

	res, err := svc.Redis.Get(ctx, s2u).Result()
	if err != nil {
		if errors.Is(err, redis.Nil) {
			return 0, nil
		} else {
			return 0, err
		}
	}

	userId, err = strconv.ParseInt(res, 10, 64)
	return userId, nil
}

func GetUserBySession(ctx context.Context, svc *svc.ServiceContext, session string) (user *models.User, err error) {
	userId, err := GetUserIdBySession(ctx, svc, session)
	if err != nil {
		return nil, err
	}
	if userId == 0 {
		return nil, nil
	}

	user = &models.User{}
	svc.Mysql.Preload("").Where("id = ?", userId).Find(user)

	return user, nil
}
